/* C++ 虚函数表
 * http://coolshell.cn/articles/12165.html
**/
#ifndef _VFT_HPP_
#define _VFT_HPP_
#include <stdio.h>

// pObj 是对象的起始地址。
// vftIndex 表示虚函数表索引，从 0 开始。
// vfIndex 表示虚函数索引，从 0 开始。
// 使用 ((size_t**)pObj)[vftIndex][vfIndex] 可访问虚函数地址。
// 将值转换成指针类型
#define _VFT_TURN_POINTER(value) reinterpret_cast<size_t const*>(value)
#define _VFT_TURN_START(pObj) reinterpret_cast<size_t const* const*>(pObj)
// 虚函数表的地址从对象的起始地址开始排列，多重继承便可能会有多个虚函数表，
// 多个虚函数表按照继承的顺序排列。
#define VFT_ADDR(pObj, vftIndex)  (_VFT_TURN_START(pObj)[vftIndex])
// 指向某个虚函数地址的指针
#define VFT_POINTER(pObj, vftIndex, vfIndex) (VFT_ADDR(pObj, vftIndex) + vfIndex)
// 获取虚函数的地址
#define VFT_FUNC_ADDR(pObj, vftIndex, vfIndex) _VFT_TURN_POINTER(VFT_ADDR(pObj, vftIndex)[vfIndex])

// 以下关于虚函数表末尾的定义，不知道以后会不会修改，目前在 64 位 Center6.9 g++4.7.2 测试通过。
void
AccessVFT(const void *pObj, int vftIndex, int vfNum = 0) {
	void (*funcPtr)() = NULL;
	size_t const *pointer;
	size_t const *addr = NULL;
	int vf = 0;

	printf("obj address:%p    [%d]th vft adress:%p\n", pObj, vftIndex, VFT_ADDR(pObj, vftIndex));
	for (; vfNum == 0 || vf < vfNum;) {
		pointer = VFT_POINTER(pObj, vftIndex, vf);
		addr = VFT_FUNC_ADDR(pObj, vftIndex, vf);
		printf("vft's [%d] vf address:%p    pointer:%p\n", vf, addr, pointer);
		if (addr == (void*)0) { // 对于单继承，表示虚函数表已经到达末尾
			printf("no more vf (single inheritance)\n");
			break;
		} else if (addr == (void*)1) { // 当前虚函数表已经到达末尾，还有下一个虚函数表，但未发现这种情况
			printf("no more vf in this vft, another one\n");
			break;
		}
		else if (reinterpret_cast<ssize_t>(addr) < 0) { // 多重继承发现虚函数表到达末尾时，虚函数地址变成了负值
			printf("no more vf (multi inheritance) addr:%p\n", addr);
			break;
		} else {
			printf("call virtual function:\n");
			funcPtr = reinterpret_cast<void(*)()>(addr); // 转换成指向无参数无返回值的函数指针
			funcPtr();
		}
		vf++;
	}
	printf("final virtual function num:%d in vftIndex:%d\n", vf, vftIndex);
	 
	 
	// 测试用
	// size_t **pVTable = (size_t**)pObj;
	// for (int i = 0; pVTable[0][i] != (size_t)NULL && i < 50; i++){
	//     printf("%d %p\n", i, (size_t*)pVTable[0][i]);
	// }
}

#endif // _VFT_HPP_
